﻿using System.Data.SqlClient;
using System;
using System.IO;
using Microsoft.DirectX.AudioVideoPlayback;
using System.Windows.Threading;
using System.Windows;
using System.Management;
using Microsoft.DirectX.DirectSound;
using System.Threading;
using System.ComponentModel;

namespace SoundServant
{
    public partial class Recorder
    {
        public event VoidEventHandler RecorderStarting;
        public event VoidEventHandler RecorderStarted;
        public event VoidEventHandler RecorderPaused;
        public event VoidEventHandler RecorderUnpaused;
        public event VoidEventHandler RecorderTracked;
        public event VoidEventHandler RecorderFinishing;
        public event VoidEventHandler RecorderFinished;
        public event VoidEventHandler RecorderCDLengthExceeded;

        TimeSpan timeIncrement = new TimeSpan(4000000);
        private TimeSpan cdLength = new TimeSpan(0, 74, 0);

        public int RecorderCurrentDiscNumber { get { return recorderCurrentDiscNumber; } }
        public int RecorderCurrentTrackNumber { get { return recorderCurrentTrackNumber; } }
        public StoredRecording Recording { get { return recording; } }
        public RecorderStates RecorderState { get { return recorderState; } }

        public void StartRecording()
        {
            if (recorderState == RecorderStates.Ready)
            {
                Logs.Information("Recorder", "Starting recording");
                recorderState = RecorderStates.Starting;
                RecorderStarting();
                LockButtons();

                recorderState = RecorderStates.Starting;
                recording = Library.NewRecording(GuessMeetingType(), congregation, this);
                Logs.Information("Recorder", "New recording created - Recording Id: " + recording.Id);
                CaptureDevicesCollection captureDevicesCollection = new CaptureDevicesCollection();
                captureDevice = new Capture(captureDevicesCollection[0].DriverGuid);

                #region Setup Capture
                short channels = 2; short bitsPerSample = 16; int samplesPerSecond = 44100;
                captureWaveFormat = new Microsoft.DirectX.DirectSound.WaveFormat();
                captureWaveFormat.Channels = channels;
                captureWaveFormat.FormatTag = WaveFormatTag.Pcm;
                captureWaveFormat.SamplesPerSecond = samplesPerSecond;
                captureWaveFormat.BitsPerSample = bitsPerSample;
                captureWaveFormat.BlockAlign = (short)(channels * (bitsPerSample / (short)8));
                captureWaveFormat.AverageBytesPerSecond = captureWaveFormat.BlockAlign * samplesPerSecond;

                captureBufferDescription = new CaptureBufferDescription();
                // Buffer is 1.2 seconds long:
                captureBufferDescription.BufferBytes = 211680;
                captureBufferDescription.Format = captureWaveFormat;
                captureBuffer = new CaptureBuffer(captureBufferDescription, captureDevice);

                captureResetEvent = new AutoResetEvent(false);
                captureNotify = new Notify(captureBuffer);
                BufferPositionNotify bufferPositionNotify1 = new BufferPositionNotify();
                bufferPositionNotify1.Offset = 70559;
                bufferPositionNotify1.EventNotifyHandle = captureResetEvent.Handle;
                BufferPositionNotify bufferPositionNotify2 = new BufferPositionNotify();
                bufferPositionNotify2.Offset = 141119;
                bufferPositionNotify2.EventNotifyHandle = captureResetEvent.Handle;
                BufferPositionNotify bufferPositionNotify3 = new BufferPositionNotify();
                bufferPositionNotify3.Offset = 211679;
                bufferPositionNotify3.EventNotifyHandle = captureResetEvent.Handle;
                captureNotify.SetNotificationPositions(new BufferPositionNotify[] { bufferPositionNotify1, bufferPositionNotify2, bufferPositionNotify3 });

                captureThreadStart = new ThreadStart(RecorderThread);
                captureThread = new Thread(captureThreadStart);
                captureThread.Priority = ThreadPriority.Highest;
                #endregion

                if (musicState != MusicStates.Ready)
                {
                    Logs.Information("Recorder", "Stopping music first");
                    MusicStop();
                }
                else
                {
                    Record();
                }
            }
        }

        private void Record()
        {
            if (recorderState == RecorderStates.Starting)
            {
                Logs.Information("Recorder", "Recording started");
                UnlockButtons();
                recorderState = RecorderStates.Recording;
                recorderTotalRecordingTime = new TimeSpan(0);
                RecorderStarted();
                RecorderDisc();
            }
        }

        void RecorderThread()
        {
            int bufferBite = 70560;
            // Buffer Length is 1.2 seconds , buffer bite is 0.4 seconds
            lock (captureLockObject)
            {
                captureStopRecording = false;
            }

            FileStream fileStream = null; BinaryWriter rdbkWriter = null; Mp3Writer mp3Writer = null;
            captureBuffer.Start(true); ushort bufferPosition = 1; int bufferOffset = 0; byte[] dataToWrite;
            MemoryStream memStream = new MemoryStream(bufferBite);
            mp3Writer = new Mp3Writer(new FileStream(recording.Directory.FullName + "//mp3.mp3", FileMode.Create), new SoundServant.WaveLib.WaveFormat(44100, 16, 2), new SoundServant.Lame.BE_CONFIG(new SoundServant.WaveLib.WaveFormat(44100, 16, 2), 56, 22050));

            while (true)
            {
                lock (captureLockObject)
                {
                    if (captureNewTrack)
                    {
                        if (captureNewDisc)
                        {
                            captureNewDisc = false;
                            recorderCurrentTrackNumber = 0;
                            recorderCurrentDiscTime = new TimeSpan(0);
                        }

                        captureNewTrack = false;
                        recorderCurrentTrackTime = new TimeSpan(0);
                        recorderCurrentTrackNumber++;

                        if (rdbkWriter != null) rdbkWriter.Close(); if (fileStream != null) fileStream.Close();
                        fileStream = new FileStream(recorderNextTrackFile, FileMode.Create); rdbkWriter = new BinaryWriter(fileStream);
                        recorderNextTrackFile = "";
                        if (RecorderTracked != null) RecorderTracked();
                        BeginLockOutTimer();
                    }
                    else if (captureStopRecording)
                    {
                        captureBuffer.Stop();
                        break;
                    }
                }

                captureResetEvent.WaitOne();
                memStream.Seek(0, SeekOrigin.Begin);
                captureBuffer.Read(bufferOffset, memStream, bufferBite, LockFlag.None);

                if (!capturePaused)
                {
                    recorderCurrentTrackTime += timeIncrement;
                    recorderCurrentDiscTime += timeIncrement;
                    recorderTotalRecordingTime += timeIncrement;

                    dataToWrite = memStream.GetBuffer();
                    rdbkWriter.Write(dataToWrite, 0, bufferBite);
                    mp3Writer.Write(dataToWrite, 0, bufferBite);
                    
                    if (recorderCurrentDiscTime + timeIncrement >= cdLength)
                    {
                        RecorderDisc();
                        RecorderCDLengthExceeded();
                    }
                }
                
                if (bufferPosition == 1)
                { bufferOffset = 70560; bufferPosition = 2; }
                else if (bufferPosition == 2)
                { bufferOffset = 141120; bufferPosition = 3; }
                else
                { bufferOffset = 0; bufferPosition = 1; }
            }

            mp3Writer.Close();
            rdbkWriter.Close();
            fileStream.Close();
        }
        public void RecorderPauseUnpause()
        {
            if (recorderState == RecorderStates.Recording && !recorderLocked)
            {
                if (capturePaused)
                {
                    RecorderUnpause();
                }
                else
                {
                    if (SSMessageBox.Show("Are you sure?", "Pause Recording", SSMessageBoxType.YesNo) == SSMessageBoxResult.Yes)
                    {
                        capturePaused = true;
                        Logs.Information("Recorder", "Recording paused");
                        if (RecorderPaused != null) RecorderPaused();
                    }
                }
            }
        }
        public void RecorderUnpause()
        {
            if (recorderState == RecorderStates.Recording && !recorderLocked)
            {
                capturePaused = false;
                Logs.Information("Recorder", "Recording unpaused");
                if (RecorderUnpaused != null) RecorderUnpaused();
            }
        }
        public void RecorderDisc()
        {
            if (recorderState == RecorderStates.Recording && !recorderLocked && !capturePaused)
            {
                Logs.Information("Recorder", "Creating a new Disc");
                captureNewDisc = true;
                recorderCurrentDiscNumber++;
                string recorderNextDiscTitle = "";
                switch (recording.Type)
                {
                    case MeetingType.SchoolServiceMeeting:
                        if (recorderCurrentDiscNumber == 1) recorderNextDiscTitle = "Book Study & School";
                        else if (recorderCurrentDiscNumber == 2) recorderNextDiscTitle = "Service Meeting";
                        break;
                    case MeetingType.TalkWatchtowerStudy:
                        if (recorderCurrentDiscNumber == 1) recorderNextDiscTitle = "Public Talk";
                        else if (recorderCurrentDiscNumber == 2) recorderNextDiscTitle = "Watchtower Study";
                        break;
                    default:
                        break;
                }

                StoredDisc lastDisc = recording.CurrentDisc;
                recording.CreateNewDisc(recorderNextDiscTitle);
                if (lastDisc != null) Logs.Information("Recorder", "Disc finsihed - Disc Id: " + lastDisc.Id + " Disc Number: " + lastDisc.Number + " Length: " + SS.ParseTimeSpan(lastDisc.Length));
                Logs.Information("Recorder", "New disc created - Disc Id: " + recording.CurrentDisc.Id + " Disc Number: " + recording.CurrentDisc.Number);
                RecorderTrack();
            }
        }
        public void RecorderTrack()
        {
            if (recorderState == RecorderStates.Recording && !recorderLocked && !capturePaused)
            {
                LockButtons();
                Logs.Information("Recorder", "Creating a new Track");
                StoredTrack lastTrack = recording.CurrentDisc.CurrentTrack;
                recorderNextTrackFile = recording.CurrentDisc.CreateNewTrack(recorderNextTrackTitle);
                if (lastTrack != null) Logs.Information("Recorder", "Track finished - Track Id: " + lastTrack.Id + " Track Number: " + lastTrack.Id + " Length: " + SS.ParseTimeSpan(lastTrack.Length));
                Logs.Information("Recorder", "New track created - Track Id: " + recording.CurrentDisc.CurrentTrack.Id + " Track File: " + recorderNextTrackFile);
                recorderNextTrackTitle = "";
                captureNewTrack = true;
                if (captureThread.ThreadState == ThreadState.Unstarted) captureThread.Start();
            }
        }

        public void RecorderTrack(string nextTrackTitle)
        {
            recorderNextTrackTitle = nextTrackTitle;
            RecorderTrack();
        }
        public void RecorderStop()
        {
            if (recorderState == RecorderStates.Recording && !recorderLocked && !capturePaused)
            {
                if (SSMessageBox.Show("Are you sure?", "Stop recording the meeting", SSMessageBoxType.YesNo) == SSMessageBoxResult.Yes)
                {
                    Logs.Information("Recorder", "Stopping recording");
                    RecorderFinishing();
                    captureStopRecording = true;
                    captureThread.Join();
                    recorderState = RecorderStates.Finished;
                    recording.Finish();
                    RecorderFinished();
                    Logs.Information("Recorder", "Recording stopped - Length: " + SS.ParseTimeSpan(recording.Length));
                }
            }
        }

        public TimeSpan RecorderTimePassed { get { return recorderTotalRecordingTime; } }
        public TimeSpan RecorderDiscTimePassed { get { return recorderCurrentDiscTime; } }
        public TimeSpan RecorderTrackTimePassed { get { return recorderCurrentTrackTime; } }
    }
}
